/**
 * 
 */
package com.android.kalaha;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

/**
 * @author nik
 * Evaluate a situation in a Kalaha game
 */
public class Evaluation extends Global{

	private Integer counter = 0;
	private Integer best_cup;
	private Integer best_difference;
	private Boolean best_cup_has_too_many_beans;
	private Boolean best_cup_is_in_optimum;
	private Integer level;
	private Boolean random_mode = true;
	private Cupboard cupboard;
	

	/**
	 * Constructor
	 */
	public Evaluation(Cupboard cupboard) {
		super();
		this.cupboard = cupboard;
	}

	
	/**
	 * Calculate the estimated values for each possible move
	 * And choose the best one
	 * @param level : how many steps should be calculated
	 */
	public void evaluate() {
		ArrayList<Integer> result = new ArrayList<Integer>();
		
		// Calculate the values for the six possibilities
		result = this.evaluate_moves();
		
		// Return the best of it
		// (Normally its the highest one, but what to do if several are equal?)
		this.decide_for_move(result);
	}

	/**
	 * Returns if the current level is the deepest one
	 * @return Boolean
	 */
	private boolean is_last_level(){
		return this.level==1;
	}
	
	/**
	 * Estimate the return of the possible moves.
	 * How much do I win, when I move cup X
	 * @return
	 */
	private ArrayList<Integer> evaluate_moves() {
		ArrayList<Integer> result = new ArrayList<Integer>();

		// Reset evaluation values
		this.counter += 1;

		// Calculate result for every possible cup 
		Iterator<Cup> itr = cupboard.get_current_player().cups.iterator();
		while (itr.hasNext()) {

			Integer difference = null;

			// Initialize new Cupboard and simulate next move
			Cup cup = (Cup) itr.next();
			Cupboard new_cupboard = this.create_cupboard();

		    if(new_cupboard.take_cup(new_cupboard.get_cup(cup.id))){

				if (this.is_last_level() || new_cupboard.get_game_over()) {

					// Last situation of a branch: Compare end results
					Integer my_future_value = new_cupboard.get_current_player()
							.get_can().get_beans();
					Integer your_future_value = new_cupboard.get_opponent_player(null).get_can().get_beans();
					difference = (my_future_value - your_future_value);
					difference = this.adapt_for_player_change(difference, new_cupboard);

				} else {

					new_cupboard.evaluation.level = (level-1);
					new_cupboard.evaluation.evaluate();

					difference = new_cupboard.evaluation.best_difference;
					difference = this.adapt_for_player_change(difference, new_cupboard);

					this.counter = new_cupboard.evaluation.counter;
				}
			}

			// Fine: Add the difference to the array: So we can find out the max
			// value later.
			this.tweet("Level: " + this.level + " Diff: " + difference);
			result.add(difference);

			// Save some memory
			new_cupboard = null;
		}
		return (result);
	}	
	
	/**
	 * 	Find out which of the six is the best
	 * (Normally its the highest one, but what to do if several are equal?)
	 * @return the best possibility (cup.id)
	 */
	private Integer decide_for_move(ArrayList<Integer> result){

		this.best_cup = null;
		this.best_difference = null;
		this.best_cup_has_too_many_beans = null;

		// We evaluated the six possibilities: Now, check which is the best one:
		
		this.tweet(result.toString());
		for (Integer z = 0; z < result.size(); z++) {
			
			Boolean replace = null;
			Boolean optimum = (this.cupboard.get_current_player().cups.get(z)
					.get_beans() == (6 - z));
			Boolean too_many_beans = (this.cupboard.get_current_player().cups.get(z)
					.get_beans() > (6 - z));


			if (result.get(z) == null) {
				continue;
			}
			
			// Replace anyway if last difference is null
			if (this.best_difference == null) {
				replace = true;

			// Replace if the difference is bigger than the last
			} else if (result.get(z) > this.best_difference) {
				replace = true;

			// If the difference is the same:
			} else if (result.get(z) == this.best_difference) {
			
				// Random mode is off: Go into details
				// Both are equal: Detail handling (Optimum and too many values)
				// Optimum means, when the last bean of the cup land in the own can.
				if(optimum){
					replace = true;
				
				}else if(!too_many_beans){
					replace = false;

				}else if(this.best_cup_is_in_optimum!=null && this.best_cup_is_in_optimum){
					replace = false;
					
				}else if(this.best_cup_has_too_many_beans==null || !this.best_cup_has_too_many_beans){
					replace = true;

				}else if (this.random_mode) {

					// Both are really equal
					// Pick up randomly, if random mode is on		
					replace = this.random_boolean();
					this.tweet("Random used");
					
				}else{

					// No difference and random is not allowed: so replace.
					replace = true;
					this.tweet("Not randomly used");
				}

			}else{
				
				// Result is smaller than the last 
				replace = false;
			}
			
			
			// replace the older value with the newest
			if (replace) {
				
				this.best_cup = this.cupboard.get_current_player().cups.get(z).id;
				this.best_cup_is_in_optimum = optimum;
				this.best_cup_has_too_many_beans = too_many_beans;
				this.best_difference = result.get(z);
			}
			this.tweet("> Replace: " + replace.toString() + " Optimum: " + optimum.toString() + " TooMany: " + too_many_beans.toString());
		}

		this.tweet("> Best Cup: " + this.best_cup.toString());

		return this.best_cup;
	}


	/**
	 * If the player changes during a simulation: invert the difference
	 * @param difference
	 * @param new_cupboard
	 * @return
	 */
	private Integer adapt_for_player_change(Integer difference, Cupboard new_cupboard){

		// Check for null
		if (difference == null) {
			return null;
		}
		
		// if player changed in the last move, invert the difference
		if (!new_cupboard.get_current_player().get_id()
				.equals(this.cupboard.get_current_player().get_id())) {
			return difference * -1;
		}		
		return difference;
	}
	
	/**
	 * Copy the Cupboard instance
	 * 
	 * @return the new instance
	 */
	private Cupboard create_cupboard() {

		// Create a new cupboard instance with the same values as the old one.
		Cupboard new_cupboard = new Cupboard();
		new_cupboard.set_game_over(false);
		new_cupboard.set_beanamount(this.cupboard.get_beanamount().toString());

		// Add same cups
		Iterator<Cup> citr = this.cupboard.get_cups().iterator();
		while (citr.hasNext()) {
			Cup cup_template = (Cup) citr.next();
			Cup cup_copy = new_cupboard.add_cup(null, cup_template.is_can());
			cup_copy.set_beans(cup_template.get_beans());
		}
		// Add the players
		new_cupboard.add_player(null);
		new_cupboard.add_player(null);
		new_cupboard.set_current_player(this.cupboard.get_current_player());

		return new_cupboard;
	}

	/**
	 * 
	 */
	public Boolean random_boolean(){
		
		Random r = new Random();
		Integer die = r.nextInt();
		die = Math.abs(die);
		if (die % 2 == 0) {
			return true;
		}
		return false;
	}
	
	/**
	 * @return the counter
	 */
	public Integer get_counter() {
		return counter;
	}


	/**
	 * @param counter the counter to set
	 */
	public void set_counter(Integer counter) {
		this.counter = counter;
	}


	/**
	 * @return the random_mode
	 */
	public Boolean get_random_mode() {
		return random_mode;
	}


	/**
	 * @param random_mode the random_mode to set
	 */
	public void set_random_mode(Boolean random_mode) {
		this.random_mode = random_mode;
	}


	/**
	 * @return the level
	 */
	public Integer get_level() {
		return level;
	}


	/**
	 * @param level the level to set
	 */
	public void set_level(Integer level) {
		this.level = level;
	}


	/**
	 * @return the best_cup
	 */
	public Integer get_best_cup() {
		return this.best_cup;
	}


	/**
	 * @param best_cup the best_cup to set
	 */
	public void set_best_cup(Integer best_cup) {
		this.best_cup = best_cup;
	}


	/**
	 * @return the best_difference
	 */
	public Integer get_best_difference() {
		return best_difference;
	}


	/**
	 * @param best_difference the best_difference to set
	 */
	public void set_best_difference(Integer best_difference) {
		this.best_difference = best_difference;
	}
}
